//////////////////////////////////////////////////////////////////////
//!\file FileSystemManager.cpp
//!\brief Does filesearch and all other file processings in a separate thread.
//!\author timmsusa@googlemail.com
//////////////////////////////////////////////////////////////////////

#include "FileSystemManager.hpp"

#include "Album.hpp"
#include <QMessageBox>
#include <QDebug>
#include <QDirIterator>
#include "Utils.hpp"


FileSystemManager::FileSystemManager(QObject *parent) :
    QObject(parent),
    m_Persistence(),
    m_DeepScanEnabled(m_Persistence.getEnableDeepScan()),
    // m_pFileStorage(new FileStorageWorkData(QDir::currentPath() + "//testData.dat")),
    m_DiffCollection(),
    m_CopyProgressCount(0),
    m_RemoveProgressCount(0),
    m_pScannerMusicCollection(),
    m_pScannerUpdateCollection(),
    m_pThreadRemoveFiles(),
    m_pThreadCopyFiles()
{

}

FileSystemManager::~FileSystemManager()
{
    //delete m_pFileStorage;
    destroyThreadCollectionFlacScan();
    destroyThreadUpdateFlacScan();
    destroyThreadCopyFiles();
    destroyThreadRemoveFiles();
}

void FileSystemManager::slotSearchInMusicCollection(QString path)
{
    qDebug() << "FileSystemManager::slotSearchInMusicCollection " << path;
    scanDirsToMusicCollection(QDir(path));
}

void FileSystemManager::slotSearchInFileIncoming(QString path)
{
    qDebug() << "FileSystemManager::slotSearchInFileIncoming " << path;
    scanDirsToUpdateCollection(QDir(path));
}

void FileSystemManager::slotCompareFlacLists()
{
    qDebug() << "FileSystemManager::slotCompareFlacLists";
    compareFlacLists();
}

void FileSystemManager::slotRemoveFromFileSystem(QStringList pathList)
{
    qDebug() << "FileSystemManager::slotRemoveFromFileSystem ";

    // Recreate thread!
    destroyThreadRemoveFiles();
    createAndConnectThreadRemoveFiles(pathList);
    m_pThreadRemoveFiles->start();
    qDebug() << "m_pThreadRemoveFiles started....";
}

// Main Comparison Algorithm.
void FileSystemManager::compareFlacLists()
{
    qDebug() << "FileSystemManager::compareFlacLists " << (m_DeepScanEnabled ? " deep-scan enabled" : " quickscan enabled");

    if ( m_pScannerUpdateCollection->getAlbumData().getAlbumList().empty())
    {
        qDebug() << "empty folders!!!\n";
        return;
    }

    m_DiffCollection.clear();
    bool duplicateFound=false;
    int count = 0;
    Album::AlbumDiffersIn diffCriterium=Album::NOTHING;

    foreach(const Album& uA, m_pScannerUpdateCollection->getAlbumData().getAlbumList())
    {
        // Reset flag.
        duplicateFound = false;
        diffCriterium = Album::NAME;

        // Start Folder Comparison.
        foreach(const Album& mA,m_pScannerMusicCollection->getAlbumData().getAlbumList())
        {
            // At first compare names
            if (uA.getName() == mA.getName())
            {
                if ( !m_DeepScanEnabled )
                {
                    // Then compare sizes
                    if (uA.getTrackList().size() == mA.getTrackList().size())
                    {
                        duplicateFound = true;
                        emit sigFlacFound( "duplicate found! " + uA.getName());
                        //qDebug() << "duplicate found " << uA.getPath() << " with size " << uA.getTrackList().size();
                    }
                }
                else // Deep Scan!
                {
                    if (uA.getFlacTrackList().size() == mA.getFlacTrackList().size())
                    {
                        // Compare Tracks and Md5 of two given Albums.
                        duplicateFound = isAlbumDuplicate(uA, mA);
                        if (!duplicateFound)
                        {
                            diffCriterium= Album::METADATA;
                        }
                    }
                }
            }
        }

        if ((!duplicateFound) )
        {
            // Hack: In Order to set diffCriterium,
            // we have to create a clone.
            Album dA = uA;
            dA.setAlbumDiffersIn(diffCriterium);
            diffCriterium = Album::NAME;

            // Add Album to diffcollection
            m_DiffCollection.push_back(dA);
            //qDebug() << QString::number(count) << " seems to be a new one...." << uIt->getPath();
            emit sigFlacFound( QString::number(count) + " update album found! " + uA.getName());
        }
    }
    emit sigFlacListComparisonFinished(m_DiffCollection);
    /*
    foreach(const Album& alb, m_DiffCollection)
    {
        if (m_DeepScanEnabled)
        {
            foreach(const FlacTrack& t, alb.getFlacTrackList())
            {
                qDebug() << "FlacTrack: " << t.m_File.completeBaseName();
            }
        }

    }
*/
}

bool FileSystemManager::isAlbumDuplicate(const Album &updateAlbum, const Album &collAlbum)
{
    bool duplicateFound = false;
    if ( updateAlbum.getFlacTrackList().size() == collAlbum.getFlacTrackList().size() )
    {
        Utils utl;
        duplicateFound = utl.compareVectorOfFlacTracks(updateAlbum.getFlacTrackStrList(), collAlbum.getFlacTrackStrList());
    }
    else
    {
        duplicateFound = false;
    }
    return duplicateFound;
}

void FileSystemManager::scanDirsToMusicCollection(const QDir &dir)
{
    destroyThreadCollectionFlacScan();
    createAndConnectThreadCollectionFlacScan(dir);
    m_pScannerMusicCollection->start();
    qDebug() << "m_pScannerMusicCollection started....";
}

void FileSystemManager::scanDirsToUpdateCollection(const QDir &dir)
{
    destroyThreadUpdateFlacScan();
    createAndConnectThreadUpdateFlacScan(dir);
    m_pScannerUpdateCollection->start();
    qDebug() << "m_pScannerUpdateCollection started...";
}

void FileSystemManager::createAndConnectThreadCollectionFlacScan(const QDir &dir)
{
    m_pScannerMusicCollection= new ThreadFlacScanner(dir);
    connect(this, SIGNAL(sigStopScanner()), m_pScannerMusicCollection, SLOT(slotStop()));
    connect(m_pScannerMusicCollection, SIGNAL(sigFlacSearchFinished()), this, SIGNAL(sigSearchInMusicCollectionFinished()));
    connect(m_pScannerMusicCollection, SIGNAL(sigAlbumFound(QString)), this, SIGNAL(sigFlacFound(QString)));
    connect(this, SIGNAL(sigEnableDeepScan(bool)), m_pScannerMusicCollection, SLOT(slotDeepScanEnabled(bool)));
}

void FileSystemManager::createAndConnectThreadUpdateFlacScan(const QDir &dir)
{
    m_pScannerUpdateCollection= new ThreadFlacScanner(dir);
    connect(this, SIGNAL(sigStopScanner()), m_pScannerUpdateCollection, SLOT(slotStop()));
    connect(m_pScannerUpdateCollection, SIGNAL(sigFlacSearchFinished()), this, SIGNAL(sigSearchInFileIncomingFinished()));
    connect(m_pScannerUpdateCollection, SIGNAL(sigAlbumFound(QString)), this, SIGNAL(sigFlacFound(QString)));
    connect(this, SIGNAL(sigEnableDeepScan(bool)), m_pScannerUpdateCollection, SLOT(slotDeepScanEnabled(bool)));

}

void FileSystemManager::createAndConnectThreadCopyFiles(const QStringList &src, const QStringList &dest)
{
    m_pThreadCopyFiles= new ThreadCopyFiles(src, dest);
    connect(m_pThreadCopyFiles, SIGNAL(sigCopyProgressCount(int,QString)), this, SIGNAL(sigCopyProgress(int, QString)));
    connect(m_pThreadCopyFiles, SIGNAL(sigCopyFinished(bool)), this, SIGNAL(sigCopyProgressFinished(bool)));
    connect(this, SIGNAL(sigStopCopy()), m_pThreadCopyFiles, SLOT(slotStopCopy()));
}

void FileSystemManager::createAndConnectThreadRemoveFiles(const QStringList &path)
{
    m_pThreadRemoveFiles= new ThreadRemoveFiles(path);
    connect(m_pThreadRemoveFiles, SIGNAL(sigRemoveProgressCount(int, QString)), this, SIGNAL(sigRemoveProgress(int, QString)));
    connect(m_pThreadRemoveFiles, SIGNAL(sigRemoveRunFinished(bool)), this, SIGNAL(sigRemoveRunFinished(bool)));
}

void FileSystemManager::destroyThreadCollectionFlacScan()
{
    if ( NULL != m_pScannerMusicCollection )
    {
        if (m_pScannerMusicCollection->isRunning())
        {
            qDebug() << "m_pScannerMusicCollection is running, so we attempt to stopp and quit, before delete!";
            emit sigStopScanUpdateCollection();
            m_pScannerMusicCollection->wait();
            m_pScannerMusicCollection->quit();
        }
        qDebug() << "Deleting old m_pScannerMusicCollection!";
        delete m_pScannerMusicCollection;
        //m_pScannerMusicCollection=NULL;
    }
    //else
    //{
    //    qDebug() << "FileSystemManager::destroyThreadCollectionFlacScan failed, because object does not exists!";
   // }
}

void FileSystemManager::destroyThreadUpdateFlacScan()
{
    if ( NULL != m_pScannerUpdateCollection )
    {
        if (m_pScannerUpdateCollection->isRunning())
        {
            qDebug() << "m_pScannerUpdateCollection is running, so we attempt to stopp and quit, before delete!";
            emit sigStopScanUpdateCollection();
            m_pScannerUpdateCollection->wait();
            m_pScannerUpdateCollection->quit();
        }
        qDebug() << "Deleting old m_pScannerUpdateCollection";
        delete m_pScannerUpdateCollection;
    }
   // else
   // {
   //     qDebug() << "FileSystemManager::destroyThreadUpdateFlacScan failed, because object does not exists!";
   // }
}

void FileSystemManager::destroyThreadCopyFiles()
{
    if ( NULL != m_pThreadCopyFiles )
    {
        if (m_pThreadCopyFiles->isRunning())
        {
            qDebug() << "m_pThreadCopyFiles is running, so we attempt to stopp and quit, before delete!";
            m_pThreadCopyFiles->wait();
            m_pThreadCopyFiles->quit();
        }
        qDebug() << "Deleting old m_pThreadCopyFiles!";
        delete m_pThreadCopyFiles;
    }
    //else
   // {
   //     qDebug() << "FileSystemManager::destroyThreadCopyFiles failed, because object does not exists!";
   // }
}

void FileSystemManager::destroyThreadRemoveFiles()
{
    if ( NULL != m_pThreadRemoveFiles )
    {
        if (m_pThreadRemoveFiles->isRunning())
        {
            qDebug() << "m_pThreadRemoveFiles is running, so we attempt to stopp and quit, before delete!";
            m_pThreadRemoveFiles->wait();
            m_pThreadRemoveFiles->quit();
        }
        qDebug() << "Deleting old m_pThreadRemoveFiles!";
        delete m_pThreadRemoveFiles;
    }
    //else
   // {
    //    qDebug() << "FileSystemManager::destroyThreadRemoveFiles failed, because object does not exists!";
   // }
}

void FileSystemManager::slotCopyAllFolderTo( QStringList srcPaths, QStringList destPaths)
{
    qDebug() << "FileSystemManager::slotCopyAllFolderTo()";

    // Recreate thread!
    destroyThreadCopyFiles();
    createAndConnectThreadCopyFiles(srcPaths, destPaths);
    m_pThreadCopyFiles->start();
    qDebug() << "New Remove-Thread created and started....";

    // Reset File Counter.
    m_CopyProgressCount = 0;
}

void FileSystemManager::slotResetCopyProgressCounter()
{
    m_CopyProgressCount = 0;
}

void FileSystemManager::slotResetRemoveProgressCounter()
{
    m_RemoveProgressCount = 0;
}

void FileSystemManager::slotEnableDeepScan(bool enable)
{
    qDebug() << "FileSystemManager::slotEnableDeepScan: " << enable;
    m_DeepScanEnabled = enable;
    emit sigEnableDeepScan(enable);
}

void FileSystemManager::slotInitOfMusicCollectionModelFinished()
{
    qDebug() << "FileSystemManager::slotInitOfAlbumModelFinished()";
    emit sigSearchInMusicCollectionFinished();
}

void FileSystemManager::slotInitOfUpdateCollectionModelFinished()
{
    qDebug() << "FileSystemManager::slotInitOfUpdateCollectionModelFinished()";
    emit sigSearchInFileIncomingFinished();
}
